package org.jeecg.modules.system.controller;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.enums.SysLogOperateType;
import org.jeecg.common.constant.enums.SysLogType;
import org.jeecg.common.system.api.ISysBaseAPI;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.DySmsEnum;
import org.jeecg.common.util.DySmsHelper;
import org.jeecg.common.util.MD5Util;
import org.jeecg.common.util.PasswordUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.encryption.EncryptedString;
import org.jeecg.common.util.sm.SmManager;
import org.jeecg.common.util.sm.UserKeyInfo;
import org.jeecg.modules.shiro.vo.DefContants;
import org.jeecg.modules.system.entity.SysDepart;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.model.SysLoginModel;
import org.jeecg.modules.system.service.ISysDepartService;
import org.jeecg.modules.system.service.ISysDictService;
import org.jeecg.modules.system.service.ISysLogService;
import org.jeecg.modules.system.service.IUserService;
import org.jeecg.modules.system.util.RandImageUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.exceptions.ClientException;
import com.sgcc.isc.ualogin.client.util.IscSSOResourceUtil;
import com.sgcc.isc.ualogin.client.vo.IscSSOUserBean;

import cn.hutool.core.util.RandomUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;

/**
 * @Author scott
 * @since 2018-12-17
 */
@RestController
@RequestMapping("/sys")
@Api(tags = "用户登录")
@Slf4j
public class LoginController {
	@Autowired
	private IUserService userService;
	@Autowired
	private ISysBaseAPI sysBaseAPI;
	@Autowired
	private ISysLogService logService;
	@Autowired
	private RedisUtil redisUtil;
	@Autowired
	private ISysDepartService sysDepartService;
	@Autowired
	private ISysDictService sysDictService;
	@Value("${mesh.loginSingle:false}")
	private Boolean loginSingle;

	private static final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890";

	@ApiOperation("登录接口")
	@RequestMapping(value = "/login")
	public Result<JSONObject> login(HttpServletRequest request, HttpServletResponse response, @RequestBody SysLoginModel sysLoginModel) {
		return userService.login(request, response, sysLoginModel);
	}

	/**
	 * 退出登录
	 * 
	 * @param request
	 * @param response
	 * @return
	 */

	@RequestMapping(value = "/logout")
	public Result<Object> logout(HttpServletRequest request, HttpServletResponse response) {
		// 用户退出逻辑
		String token = request.getHeader(DefContants.X_ACCESS_TOKEN);
		
		// 查找token
		String username = null;
		if (oConvertUtils.isEmpty(token)) {
			// log.info("isc的user是："+);
			try {
				if (loginSingle) {
					IscSSOUserBean iscSSOUserBean = IscSSOResourceUtil.getIscUserBean(request);
					username = iscSSOUserBean.getIscUserSourceId();
				} else {
					return Result.ok("退出登录失败！");
				}
			} catch (Exception e) {
				log.info("isc get user Fail");
			}
		} else {
			username = JwtUtil.getUsername(token);
		}
		
		if (oConvertUtils.isEmpty(username)) {
			return Result.ok("退出登录失败！");
		}
		

		LoginUser sysUser = sysBaseAPI.getUserByName(username);
		if (sysUser != null) {
			String tokenKey = CommonConstant.PREFIX_USER_TOKEN + sysUser.getUsername() + "_" + token;
			// if(loginSingle) {
			// //判是否还有在线的token，和删除当前token
			// boolean hasOthers =
			// redisUtil.hasOtherUser(CommonConstant.PREFIX_USER_TOKEN+username,
			// tokenKey);
			// if(hasOthers) {
			// // 清空当前用户登录Token缓存
			// redisUtil.del(CommonConstant.PREFIX_USER_TOKEN+"_"+sysUser.getUsername()+"_"
			// + token);
			// return Result.ok("退出登录成功！");
			// }else {
			// userService.logout(sysUser);
			// }
			// }else {
			// userService.logout(sysUser);
			// }
			if (loginSingle) {
				String key = CommonConstant.PREFIX_USER_TOKEN + sysUser.getUsername() + "_" + token;
				Object current = redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + sysUser.getUsername() + "_" + token);
				if (current != null) {
					redisUtil.batchDel(CommonConstant.PREFIX_USER_TOKEN + username);
					userService.logout(sysUser);
					// 清空用户登录Shiro权限缓存
					redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());
					// 清空用户的缓存信息（包括部门信息），例如sys:cache:user::<username>
					redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername()));
				} else if (!redisUtil.hasOtherUser(CommonConstant.PREFIX_USER_TOKEN + username, key)) {
					// 没有用户了就退出isc
					userService.logout(sysUser);
				}
			} else {
				userService.logout(sysUser);
				// 清空用户登录Shiro权限缓存
				redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());
				// 清空用户的缓存信息（包括部门信息），例如sys:cache:user::<username>
				redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername()));
			}
			sysBaseAPI.addLog("用户名: " + sysUser.getUsername() + ",退出成功！", SysLogType.LOGIN, SysLogOperateType.LOGOUT, sysUser.getId());
			log.info(" 用户名:  " + sysUser.getUsername() + ",退出成功！ ");
			// 清空用户登录Token缓存
			redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + "_" + sysUser.getUsername() + "_" + token);
			// 调用shiro的logout
			SecurityUtils.getSubject().logout();

			// 国密 start
			destorySm(sysUser.getId(), sysUser.getUsername());
			// 国密 end

			request.removeAttribute("_const_cas_assertion_");
			request.getSession().invalidate();

			return Result.ok("退出登录成功！");
		} else {
			return Result.error("Token无效!");
		}
	}

	/**
	 * 获取访问量
	 * 
	 * @return
	 */
	@GetMapping("loginfo")
	public Result<JSONObject> loginfo() {
		Result<JSONObject> result = new Result<JSONObject>();
		JSONObject obj = new JSONObject();
		// update-begin--Author:zhangweijian Date:20190428 for：传入开始时间，结束时间参数
		// 获取一天的开始和结束时间
		Calendar calendar = new GregorianCalendar();
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		Date dayStart = calendar.getTime();
		calendar.add(Calendar.DATE, 1);
		Date dayEnd = calendar.getTime();
		// 获取系统访问记录
		Long totalVisitCount = logService.findTotalVisitCount();
		obj.put("totalVisitCount", totalVisitCount);
		Long todayVisitCount = logService.findTodayVisitCount(dayStart, dayEnd);
		obj.put("todayVisitCount", todayVisitCount);
		Long todayIp = logService.findTodayIp(dayStart, dayEnd);
		// update-end--Author:zhangweijian Date:20190428 for：传入开始时间，结束时间参数
		obj.put("todayIp", todayIp);
		result.setResult(obj);
		result.success("登录成功");
		return result;
	}

	/**
	 * 获取访问量
	 * 
	 * @return
	 */
	@GetMapping("visitInfo")
	public Result<List<Map<String, Object>>> visitInfo() {
		Result<List<Map<String, Object>>> result = new Result<List<Map<String, Object>>>();
		Calendar calendar = new GregorianCalendar();
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		calendar.add(Calendar.DAY_OF_MONTH, 1);
		Date dayEnd = calendar.getTime();
		calendar.add(Calendar.DAY_OF_MONTH, -7);
		Date dayStart = calendar.getTime();
		List<Map<String, Object>> list = logService.findVisitCount(dayStart, dayEnd);
		result.setResult(oConvertUtils.toLowerCasePageList(list));
		return result;
	}

	/**
	 * 登陆成功选择用户当前部门
	 * 
	 * @param user
	 * @return
	 */
	@RequestMapping(value = "/selectDepart", method = RequestMethod.PUT)
	public Result<JSONObject> selectDepart(@RequestBody SysUser user) {
		Result<JSONObject> result = new Result<JSONObject>();
		String username = user.getUsername();
		if (oConvertUtils.isEmpty(username)) {
			LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
			username = sysUser.getUsername();
		}
		String orgCode = user.getOrgCode();
		this.userService.updateUserDepart(username, orgCode);
		SysUser sysUser = userService.getUserByName(username);
		JSONObject obj = new JSONObject();
		obj.put("userInfo", sysUser);
		result.setResult(obj);
		return result;
	}

	/**
	 * 短信登录接口
	 * 
	 * @param jsonObject
	 * @return
	 */
	@PostMapping(value = "/sms")
	public Result<String> sms(@RequestBody JSONObject jsonObject) {
		Result<String> result = new Result<String>();
		String mobile = jsonObject.get("mobile").toString();
		// 手机号模式 登录模式: "2" 注册模式: "1"
		String smsmode = jsonObject.get("smsmode").toString();
		log.info(mobile);
		if (oConvertUtils.isEmpty(mobile)) {
			result.setMessage("手机号不允许为空！");
			result.setSuccess(false);
			return result;
		}
		Object object = redisUtil.get(mobile);
		if (object != null) {
			result.setMessage("验证码10分钟内，仍然有效！");
			result.setSuccess(false);
			return result;
		}

		// 随机数
		String captcha = RandomUtil.randomNumbers(6);
		JSONObject obj = new JSONObject();
		obj.put("code", captcha);
		try {
			boolean b = false;
			// 注册模板
			if (CommonConstant.SMS_TPL_TYPE_1.equals(smsmode)) {
				SysUser sysUser = userService.getUserByPhone(mobile);
				if (sysUser != null) {
					result.error500(" 手机号已经注册，请直接登录！");
					sysBaseAPI.addLog("手机号已经注册，请直接登录！", CommonConstant.LOG_TYPE_LOGIN, null);
					return result;
				}
				b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.REGISTER_TEMPLATE_CODE);
			} else {
				// 登录模式，校验用户有效性
				SysUser sysUser = userService.getUserByPhone(mobile);
				result = userService.checkUserIsEffective(sysUser);
				if (!result.isSuccess()) {
					String message = result.getMessage();
					if ("该用户不存在，请注册".equals(message)) {
						result.error500("该用户不存在或未绑定手机号");
					}
					return result;
				}

				/**
				 * smsmode 短信模板方式 0 .登录模板、1.注册模板、2.忘记密码模板
				 */
				if (CommonConstant.SMS_TPL_TYPE_0.equals(smsmode)) {
					// 登录模板
					b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.LOGIN_TEMPLATE_CODE);
				} else if (CommonConstant.SMS_TPL_TYPE_2.equals(smsmode)) {
					// 忘记密码模板
					b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.FORGET_PASSWORD_TEMPLATE_CODE);
				}
			}

			if (b == false) {
				result.setMessage("短信验证码发送失败,请稍后重试");
				result.setSuccess(false);
				return result;
			}
			// 验证码10分钟内有效
			redisUtil.set(mobile, captcha, 600);
			// update-begin--Author:scott Date:20190812 for：issues#391
			// result.setResult(captcha);
			// update-end--Author:scott Date:20190812 for：issues#391
			result.setSuccess(true);

		} catch (ClientException e) {
			e.printStackTrace();
			result.error500(" 短信接口未配置，请联系管理员！");
			return result;
		}
		return result;
	}

	/**
	 * 手机号登录接口
	 * 
	 * @param jsonObject
	 * @return
	 */
	@ApiOperation("手机号登录接口")
	@PostMapping("/phoneLogin")
	public Result<JSONObject> phoneLogin(@RequestBody JSONObject jsonObject) {
		Result<JSONObject> result = new Result<JSONObject>();
		String phone = jsonObject.getString("mobile");

		// 校验用户有效性
		SysUser sysUser = userService.getUserByPhone(phone);
		result = userService.checkUserIsEffective(sysUser);
		if (!result.isSuccess()) {
			return result;
		}

		String smscode = jsonObject.getString("captcha");
		Object code = redisUtil.get(phone);
		if (!smscode.equals(code)) {
			result.setMessage("手机验证码错误");
			return result;
		}
		// 用户信息
		userService.userInfo(sysUser, result);
		// 添加日志
		sysBaseAPI.addLog("用户名: " + sysUser.getUsername() + ",登录成功！", CommonConstant.LOG_TYPE_LOGIN, null);

		return result;
	}

	/**
	 * 获取加密字符串
	 * 
	 * @return
	 */
	@GetMapping(value = "/getEncryptedString")
	public Result<Map<String, String>> getEncryptedString() {
		Result<Map<String, String>> result = new Result<Map<String, String>>();
		Map<String, String> map = new HashMap<String, String>();
		map.put("key", EncryptedString.key);
		map.put("iv", EncryptedString.iv);
		result.setResult(map);
		return result;
	}

	/**
	 * 后台生成图形验证码 ：有效
	 * 
	 * @param response
	 * @param key
	 */
	@ApiOperation("获取验证码")
	@GetMapping(value = "/randomImage/{key}")
	public Result<String> randomImage(HttpServletResponse response, @PathVariable String key) {
		Result<String> res = new Result<String>();
		try {
			String code = RandomUtil.randomString(BASE_CHECK_CODES, 4);
			String lowerCaseCode = code.toLowerCase();
			String realKey = MD5Util.MD5Encode(lowerCaseCode + key, "utf-8");
			redisUtil.set(realKey, lowerCaseCode, 60);
			String base64 = RandImageUtil.generate(code);
			res.setSuccess(true);
			res.setResult(base64);
		} catch (Exception e) {
			res.error500("获取验证码出错" + e.getMessage());
			e.printStackTrace();
		}
		return res;
	}

	/**
	 * app登录
	 * 
	 * @param sysLoginModel
	 * @return
	 * @throws Exception
	 */
	@RequestMapping(value = "/mLogin", method = RequestMethod.POST)
	public Result<JSONObject> mLogin(@RequestBody SysLoginModel sysLoginModel) throws Exception {
		Result<JSONObject> result = new Result<JSONObject>();
		String username = sysLoginModel.getUsername();
		String password = sysLoginModel.getPassword();

		// 1. 校验用户是否有效
		SysUser sysUser = userService.getUserByName(username);
		result = userService.checkUserIsEffective(sysUser);
		if (!result.isSuccess()) {
			return result;
		}

		// 2. 校验用户名或密码是否正确
		String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
		String syspassword = sysUser.getPassword();
		if (!syspassword.equals(userpassword)) {
			result.error500("用户名或密码错误");
			return result;
		}

		String orgCode = sysUser.getOrgCode();
		if (oConvertUtils.isEmpty(orgCode)) {
			// 如果当前用户无选择部门 查看部门关联信息
			List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
			if (departs == null || departs.size() == 0) {
				result.error500("用户暂未归属部门,不可登录!");
				return result;
			}
			orgCode = departs.get(0).getOrgCode();
			sysUser.setOrgCode(orgCode);
			this.userService.updateUserDepart(username, orgCode);
		}
		JSONObject obj = new JSONObject();
		// 用户登录信息
		obj.put("userInfo", sysUser);

		// 生成token
		String token = JwtUtil.sign(username, syspassword);
		// 设置超时时间
		redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + username + "_" + token, token);
		redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + username + "_" + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
		// token 信息
		obj.put("token", token);
		result.setResult(obj);
		result.setSuccess(true);
		result.setCode(200);
		sysBaseAPI.addLog("用户名: " + username + ",登录成功[移动端]！", CommonConstant.LOG_TYPE_LOGIN, null);
		return result;
	}

	/**
	 * 图形验证码
	 * 
	 * @param sysLoginModel
	 * @return
	 */
	@RequestMapping(value = "/checkCaptcha", method = RequestMethod.POST)
	public Result<?> checkCaptcha(@RequestBody SysLoginModel sysLoginModel) {
		String captcha = sysLoginModel.getCaptcha();
		String checkKey = sysLoginModel.getCheckKey();
		if (captcha == null) {
			return Result.error("验证码无效");
		}
		String lowerCaseCaptcha = captcha.toLowerCase();
		String realKey = MD5Util.MD5Encode(lowerCaseCaptcha + checkKey, "utf-8");
		Object checkCode = redisUtil.get(realKey);
		if (checkCode == null || !checkCode.equals(lowerCaseCaptcha)) {
			return Result.error("验证码错误");
		}
		return Result.ok();
	}

	private void destorySm(String userId, String username) {
		log.info("----sm manager destory  ----");
		try {
			String key = SmManager.SM + userId;
			UserKeyInfo userKey = (UserKeyInfo) redisUtil.get(key);
			log.info("[SM][LoginController][destorySm] 用户{}【{}】退出，删除用户的国密秘钥", username, userId);
			if (userKey != null) {
				redisUtil.del(userKey.getUserKey());
			}
		} catch (Exception e) {
			log.error("----sm manager destory error ----");
		}
	}

	@RequestMapping(value = "/checkLogin", method = RequestMethod.POST)
	public Result<?> checkLogin(HttpServletRequest request, HttpServletResponse response) {
		String token = request.getHeader(DefContants.X_ACCESS_TOKEN);
		if (token == null) {
			response.setStatus(500);
			return Result.error(500, "Token失效，请重新登录");

		}
		String username = JwtUtil.getUsername(token);
		if (username == null) {
			response.setStatus(500);
			return Result.error(500, "Token失效，请重新登录");

		}
		Object current = redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + username + "_" + token);
		if (current == null) {
			response.setStatus(500);
			return Result.error(500, "Token失效，请重新登录");
		}
		return Result.ok();
	}

}